﻿#include "FaceModule.h"

#include <QDataStream>
#include <QtSerialPort/QSerialPortInfo>
#include <QTimer>

#include "FaceModule/Message.h"

FaceModule::FaceModule(QObject *parent) : QObject(parent)
{
    m_serial = new QSerialPort(this);
    connect(m_serial, &QSerialPort::readyRead, this, &FaceModule::onParseReply, Qt::QueuedConnection);

    QTimer::singleShot(1000, this, [this](){
        QString errInfo;
        openSerialPort(115200, errInfo);
        emit sigShowMsg(errInfo);
    });

    return;
}


bool FaceModule::openSerialPort(qint32 baudRate, QString &errInfo)
{
    if(m_serial && m_serial->isOpen()){
        m_serial->close();
    }

    QString portName;
    QList<QSerialPortInfo> portList = QSerialPortInfo::availablePorts();
    auto it = portList.begin();
    for (; it != portList.end(); ++it) {
        portName = it->portName();
        m_serial->setPortName(portName); //设置端口名称.
        m_serial->setBaudRate(QSerialPort::Baud115200); //设置波特率bps，模块默认115200bps.
        m_serial->setParity(QSerialPort::NoParity); //设置校验位，NoParity表示不校验.
        m_serial->setDataBits(QSerialPort::Data8); //设置数据位.
        m_serial->setStopBits(QSerialPort::OneStop); //设置停止位.
        m_serial->setFlowControl(QSerialPort::NoFlowControl); //设置流控.

        if (!m_serial->open(QIODevice::ReadWrite)) {
            errInfo = QString::fromLocal8Bit("[提示]串口%1打开失败，原因%2").arg(portName).arg(m_serial->errorString());
            continue;
        }
        QByteArray cmd("\xEF\xAA\x10\x00\x00\x10", 6);
        m_serial->write(cmd);
        m_serial->waitForBytesWritten(200);
        m_serial->waitForReadyRead(600);
        QByteArray temp = m_serial->readAll();
        if ((8 == temp.size()) && (0 == temp[2])) {
            break;
        }
        m_serial->close();
    }

    if (it == portList.end()) {
        errInfo = QString::fromLocal8Bit("[错误]未探测到合法的人脸串口设备");
        emit sigShowMsg(errInfo);
        return false;
    }

    errInfo = QString::fromLocal8Bit("[提示]人脸模块串口%1已打开，波特率%2").arg(portName).arg(baudRate);
    emit sigShowMsg(errInfo);
    return true;
}


void FaceModule::onSendCommand(uint8_t mid, void *param, int paramSize)
{
    const s_msg_verify *verify = nullptr;
    const s_msg_enroll *enroll = nullptr;
    const s_msg_delete_user *deleteUser = nullptr;
    const s_msg_enroll_itg *enrollItg = nullptr;
    QByteArray cmd;
    uint16_t dataSize = paramSize;
    uint8_t sum = 0;
    QDataStream stream(&cmd, QIODevice::WriteOnly);
    stream.setByteOrder(QDataStream::BigEndian);
    stream.writeRawData("\xEF\xAA", 2);
    stream << mid;
    switch(mid)
    {
    case MID_RESET:
        stream << dataSize << sum;
        break;
    case MID_GET_STATUS:
        stream << dataSize << sum;
        break;
    case MID_VERIFY:
        dataSize = sizeof(s_msg_verify);
        verify = reinterpret_cast<s_msg_verify*>(param);
        stream << dataSize << verify->pd_rightaway << verify->timeout << sum;
        break;
    case MID_ENROLL:
        dataSize = sizeof(s_msg_enroll);
        enroll = reinterpret_cast<s_msg_enroll*>(param);
        stream << dataSize << enroll->admin;
        stream.writeRawData(reinterpret_cast<const char*>(enroll->user_name), FACE_USER_NAME_SIZE);
        stream << enroll->face_direction << enroll->timeout << sum;
        break;
    case MID_ENROLL_SINGLE:
        dataSize = sizeof(s_msg_enroll);
        enroll = reinterpret_cast<s_msg_enroll*>(param);
        stream << dataSize << enroll->admin;
        stream.writeRawData(reinterpret_cast<const char*>(enroll->user_name), FACE_USER_NAME_SIZE);
        stream << enroll->face_direction << enroll->timeout << sum;
        break;
    case MID_DELETE_USER:
        dataSize = sizeof(s_msg_delete_user);
        deleteUser = reinterpret_cast<s_msg_delete_user*>(param);
        stream << dataSize << deleteUser->user_id_heb << deleteUser->user_id_leb << sum;
        break;
    case MID_DELETE_ALL:
        stream << dataSize << sum;
        break;
    case MID_GET_ALL_USERID:
        stream << dataSize << sum;
        break;
    case MID_ENROLL_ITG:
        dataSize = sizeof(s_msg_enroll_itg);
        enrollItg = reinterpret_cast<s_msg_enroll_itg*>(param);
        stream << dataSize << enrollItg->admin;
        stream.writeRawData(reinterpret_cast<const char*>(enrollItg->user_name), FACE_USER_NAME_SIZE);
        stream << enrollItg->face_direction << enrollItg->enroll_type << enrollItg->enable_duplicate;
        stream.writeRawData(reinterpret_cast<const char*>(enrollItg->reserved), 3);
        stream << sum;
        break;
    }
    modifyParityCheck(cmd);

    m_serial->write(cmd);
    QString cmdStr = getCmdStr(mid);
    QString content = QString(cmd.toHex(' ')).toUpper();
    QString msg = QString::fromLocal8Bit("[指令]%1:%2").arg(cmdStr).arg(content);
    emit sigShowMsg(msg);
    //qDebug() << QString(cmd.toHex());
}

void FaceModule::onParseReply()
{
    const s_msg_head *head = nullptr;
    const s_reply_head *reply = nullptr;
    const s_reply_verify *verify = nullptr;
    const s_reply_enroll *enroll = nullptr;
    const s_reply_all_userid *allUserId = nullptr;
    const s_note_head *note = nullptr;
    uint8_t faceDirection = 0;
    uint16_t userId = 0;
    QByteArray userName;
    QByteArray allUserIdByte;
    const int headSize = sizeof(s_msg_head);
    bool flag = false;

    QString msg;
    m_buffer.append(m_serial->readAll());
    while(m_buffer.size() > headSize){
        head = reinterpret_cast<s_msg_head *>(m_buffer.data());
        if(0 != memcmp(head, "\xEF\xAA", 2)){
            m_buffer.remove(0, 1);
            continue;
        }

        unsigned short length = (head->len[0] << 8) | head->len[1];
        if(m_buffer.size() < headSize + length + 1){
            break;
        }

        QString content;
        QString cmdStr = getCmdStr(head->mid);
        QString resultStr;
        QString statusStr;
        QString faceStr;
        switch(head->mid){
        case MID_REPLY:
            content = QString(m_buffer.left(headSize + length + 1).toHex(' ')).toUpper();
            reply = reinterpret_cast<s_reply_head *>(m_buffer.data() + sizeof(s_msg_head));
            cmdStr = getCmdStr(reply->mid);
            resultStr = getResultStr(reply->result);
            flag = true;
            if(MR_SUCCESS != reply->result){ //应答反馈了错误还要下解析了.
                msg = QString::fromLocal8Bit("[错误]%1执行失败:%2，%3").arg(cmdStr, resultStr, content);
                emit sigShowMsg(msg);
                flag = false;
            }

            switch (reply->mid) {
            case MID_RESET:
                msg = QString::fromLocal8Bit("[应答]%1|%2|%3").arg(cmdStr, resultStr, content);
                emit sigShowMsg(msg);
                break;
            case MID_GET_STATUS:
                statusStr = getStatusStr(reply->data[0]);
                msg = QString::fromLocal8Bit("[应答]%1|%2|状态=%3|%4").arg(cmdStr, resultStr, statusStr, content);
                emit sigGetStatus(reply->data[0], resultStr, flag);
                emit sigShowMsg(msg);
                break;
            case MID_VERIFY:
                verify = reinterpret_cast<const s_reply_verify *>(reply->data);
                userId = (verify->user_id_heb << 8) | verify->user_id_leb;
                userName = QByteArray(reinterpret_cast<const char*>(verify->user_name), FACE_USER_NAME_SIZE);
                emit sigVerify(userId, resultStr, flag);
                msg = QString::fromLocal8Bit("[应答]%1|%2|ID=%3|%4").arg(cmdStr, resultStr).arg(userId).arg(content);
                emit sigShowMsg(msg);
                break;
            case MID_ENROLL:
            case MID_ENROLL_SINGLE:
                enroll = reinterpret_cast<const s_reply_enroll *>(reply->data);
                userId = (enroll->user_id_heb << 8) | enroll->user_id_leb;
                emit sigEnroll(userId, resultStr, flag);
                msg = QString::fromLocal8Bit("[应答]%1|%2|ID=%3|%4").arg(cmdStr, resultStr).arg(userId).arg(content);
                emit sigShowMsg(msg);
                break;
            case MID_DELETE_USER:
//                emit sigDeleteUserOver();
                msg = QString::fromLocal8Bit("[应答]%1|%2|%3").arg(cmdStr, resultStr, content);
                emit sigShowMsg(msg);
                break;
            case MID_DELETE_ALL:
                emit sigDeleteAllUser(resultStr, flag);
                msg = QString::fromLocal8Bit("[应答]%1|%2|%3").arg(cmdStr, resultStr, content);
                emit sigShowMsg(msg);
                break;
            case MID_GET_ALL_USERID:
                allUserId = reinterpret_cast<const s_reply_all_userid *>(reply->data);
                allUserIdByte = QByteArray(reinterpret_cast<const char *>(allUserId->users_id), allUserId->user_counts*2);
                emit sigGetAllUserId(allUserIdByte, allUserId->user_counts, resultStr, flag);
                msg = QString::fromLocal8Bit("[应答]%1|%2|%3").arg(cmdStr, resultStr, content);
                emit sigShowMsg(msg);
                break;
            case MID_ENROLL_ITG:
                enroll = reinterpret_cast<const s_reply_enroll *>(reply->data);
                userId = (enroll->user_id_heb << 8) | enroll->user_id_leb;
                emit sigEnroll(userId, resultStr, flag);
                msg = QString::fromLocal8Bit("[应答]%1|%2|ID=%3|%4").arg(cmdStr, resultStr).arg(userId).arg(content);
                emit sigShowMsg(msg);
                break;
            default:
                msg = QString::fromLocal8Bit("[未知应答]%1").arg(content);
                emit sigShowMsg(msg);
            }
            break;
        case MID_NOTE:
            content = QString(m_buffer.left(headSize + length + 1).toHex(' ')).toUpper();
            note = reinterpret_cast<s_note_head *>(m_buffer.data() + sizeof(s_msg_head));
            switch(note->nid){
            case NOTE_ID_READY:
                msg = QString::fromLocal8Bit("[NOTE]模块就绪%1").arg(content);
                emit sigShowMsg(msg);
                break;
            case NOTE_ID_FACE_STATE:
                faceStr = getFaceStatuStr(note->data[0]);
                msg = QString::fromLocal8Bit("[NOTE]人脸状态=%1|%2").arg(faceStr, content);
                emit sigShowMsg(msg);
                break;
            case NOTE_ID_UNKNOWN_ERROR:
                msg = QString::fromLocal8Bit("[NOTE]未知错误|%1").arg(content);
                emit sigShowMsg(msg);
                break;
            case NOTE_ID_OTA_DONE:
                msg = QString::fromLocal8Bit("[NOTE]OTA升级完毕|%1").arg(content);
                emit sigShowMsg(msg);
                break;
            case NOTE_ID_EYE_STATE:
                msg = QString::fromLocal8Bit("[NOTE]解锁过程中睁闭眼状态|%1").arg(content);
                emit sigShowMsg(msg);
                break;
            default:
                msg = QString::fromLocal8Bit("[未知NOTE]%1").arg(content);
                emit sigShowMsg(msg);
            }
            break;
        case MID_IMAGE:
            break;
        }
        m_buffer.remove(0, headSize + length + 1);
    }
}


QString FaceModule::getStatusStr(uint8_t status)
{
    QString ret;
    switch(status){
    case MS_STANDBY:
        ret = QString::fromLocal8Bit("空闲，等待指令中");
        break;
    case MS_BUSY:
        ret = QString::fromLocal8Bit("繁忙中，正在工作或处理指令");
        break;
    case MS_ERROR:
        ret = QString::fromLocal8Bit("出错状态，可能不会工作");
        break;
    case MS_INVALID:
        ret = QString::fromLocal8Bit("未初始化");
        break;
    case MS_OTA:
        ret = QString::fromLocal8Bit("处于OTA状态");
        break;
    }
    return ret;
}

QString FaceModule::getCmdStr(uint8_t mid)
{
    QString ret;
    switch(mid)
    {
    case MID_REPLY:          ret = QString::fromLocal8Bit("应答"); break;
    case MID_NOTE:           ret = QString::fromLocal8Bit("提示"); break;
    case MID_IMAGE:          ret = QString::fromLocal8Bit("图像"); break;
    case MID_RESET:          ret = QString::fromLocal8Bit("复位"); break;
    case MID_GET_STATUS:     ret = QString::fromLocal8Bit("获取状态"); break;
    case MID_VERIFY:         ret = QString::fromLocal8Bit("鉴权解锁"); break;
    case MID_ENROLL:         ret = QString::fromLocal8Bit("交互式注册"); break;
    case MID_ENROLL_SINGLE:  ret = QString::fromLocal8Bit("单帧注册"); break;
    case MID_DELETE_USER:    ret = QString::fromLocal8Bit("删除指定用户"); break;
    case MID_DELETE_ALL:     ret = QString::fromLocal8Bit("删除所有用户"); break;
    case MID_GET_ALL_USERID: ret = QString::fromLocal8Bit("获取所有用户"); break;
    case MID_ENROLL_ITG: ret = QString::fromLocal8Bit("集成式注册"); break;
    default: ret = QString::fromLocal8Bit("出现未知指令码%1H").arg((unsigned short)(mid), 4, 16);
    }
    return ret;
}

QString FaceModule::getResultStr(uint8_t result)
{
    QString ret;
    switch(result)
    {
    case MR_SUCCESS:            ret = QString::fromLocal8Bit("成功"); break;
    case MR_FAILED_REJECTED:    ret = QString::fromLocal8Bit("模组拒绝该命令"); break;
    case MR_FAILED_ABORTED:     ret = QString::fromLocal8Bit("注册/解锁算法已终止"); break;
    case MR_FAILED_CAMERA:      ret = QString::fromLocal8Bit("相机打开失败"); break;
    case MR_FAILED_UNKNOWN_REASON:  ret = QString::fromLocal8Bit("未知错误"); break;
    case MR_FAILED_INVALID_PARAM:   ret = QString::fromLocal8Bit("无效的参数"); break;
    case MR_FAILED_NO_MEMORY:       ret = QString::fromLocal8Bit("内存不足"); break;
    case MR_FAILED_UNKNOWN_USER:    ret = QString::fromLocal8Bit("未注册的用户"); break;
    case MR_FAILED_MAXUSER:         ret = QString::fromLocal8Bit("注册超过最大用户数量"); break;
    case MR_FAILED_FACE_ENROLLED:   ret = QString::fromLocal8Bit("人脸已注册"); break;
    case MR_FAILED_LIVENESS_CHECK:  ret = QString::fromLocal8Bit("活体检测失败"); break;
    case MR_FAILED_TIMEOUT:         ret = QString::fromLocal8Bit("注册或解锁超时"); break;
    case MR_FAILED_AUTHORIZATION:   ret = QString::fromLocal8Bit("加密芯片授权失败"); break;
    case MR_FAILED_READ_FILE:       ret = QString::fromLocal8Bit("读文件失败"); break;
    case MR_FAILED_WRITE_FILE:      ret = QString::fromLocal8Bit("写文件失败"); break;
    case MR_FAILED_NO_ENCRYPT:      ret = QString::fromLocal8Bit("通信协议未加密"); break;
    case MR_FAILED_NO_RGBIMAGE:     ret = QString::fromLocal8Bit("RGB图像没有ready"); break;
    default: ret = QString::fromLocal8Bit("出现未知结果码%1H").arg((unsigned short)(result), 4, 16);
    }
    return ret;
}

QString FaceModule::getFaceStatuStr(uint8_t status)
{
    QString ret;
    switch(status)
    {
    case FACE_STATE_NORMAL: ret = QString::fromLocal8Bit("人脸正常"); break;
    case FACE_STATE_NOFACE: ret = QString::fromLocal8Bit("未检测到人脸"); break;
    case FACE_STATE_TOO_UP: ret = QString::fromLocal8Bit("人脸太靠近图片上边沿"); break;
    case FACE_STATE_TOO_DOWN: ret = QString::fromLocal8Bit("人脸太靠近图片下边沿"); break;
    case FACE_STATE_TOO_LEFT: ret = QString::fromLocal8Bit("人脸太靠近图片左边沿"); break;
    case FACE_STATE_TOO_RIGHT: ret = QString::fromLocal8Bit("人脸太靠近图片右边沿"); break;
    case FACE_STATE_TOO_FAR: ret = QString::fromLocal8Bit("人脸距离太远"); break;
    case FACE_STATE_TOO_CLOSE: ret = QString::fromLocal8Bit("人脸距离太近"); break;
    case FACE_STATE_EYEBROW_OCCLUSION: ret = QString::fromLocal8Bit("眉毛遮挡"); break;
    case FACE_STATE_EYE_OCCLUSION: ret = QString::fromLocal8Bit("眼睛遮挡"); break;
    case FACE_STATE_FACE_OCCLUSION: ret = QString::fromLocal8Bit("人脸遮挡"); break;
    case FACE_STATE_DIRECTION_ERROR: ret = QString::fromLocal8Bit("注册人脸方向错误"); break;
    case FACE_STATE_EYE_CLOSE_STATUS_OPEN_EYE: ret = QString::fromLocal8Bit("在闭眼模式检测到睁眼状态"); break;
    case FACE_STATE_EYE_CLOSE_STATUS: ret = QString::fromLocal8Bit("确认闭眼状态"); break;
    case FACE_STATE_EYE_CLOSE_UNKNOW_STATUS: ret = QString::fromLocal8Bit("在闭眼模式检测中无法判定睁闭眼状态"); break;
    default: ret = QString::fromLocal8Bit("出现未知人脸状态%1H").arg((unsigned short)(status), 4, 16);
    }
    return ret;
}

void FaceModule::modifyParityCheck(QByteArray &cmd)
{
    quint16 sum = 0;
    const int len = cmd.size();
    const quint8 *ptr = reinterpret_cast<const quint8 *>(cmd.data());
    for(int i=2; i<len-1; ++i){
        sum = sum ^ ptr[i];
        if(len-2 == i){
            cmd[len-1] = sum;
        }
    }
    return;
}

